using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NHibernate;
using NHibernate.Linq;
using System.Linq;
using Kotonoha.Server.Extensions;

namespace Kotonoha.Objects {
    public abstract class PersistentObjectHolder<T> where T:class, IIdentifable {
        protected ObservableCollection<T> collection;

        protected Dictionary<long, T> cache;

        protected ISession session;

        protected PersistentObjectHolder() {
            Load();
        }

        private void Load() {
            session = CreateSession();
            collection = new ObservableCollection<T>(EnumerateItems());
            cache = collection.ToDictionary(i => i.Id, i => i);
        }

        protected virtual IEnumerable<T> EnumerateItems() {
            return session.Linq<T>();
        }

        protected abstract ISession CreateSession();

        public virtual ObservableCollection<T> Collection {
            get { return collection; }
        }

        public virtual T this[long id] {
            get {
                return cache[id];
            }
        }

        public virtual void ProcessInTransaction(IEnumerable<T> tEnum, Action<T> action) {
            using (var t = session.BeginTransaction()) {
                foreach (var val in tEnum) {
                    action(val);
                }
                t.Commit();
            }
        }

        public virtual void Process(bool inTransaction, IEnumerable<T> tEnum, Action<T> action, Action<T> preAction, Action<T> postAction) {
            if (preAction != null) {
                tEnum.ForEach(preAction);
            }
            if (inTransaction) {
                ProcessInTransaction(tEnum, action);
            } else {
                tEnum.ForEach(action);
            }
            if (postAction != null) {
                tEnum.ForEach(postAction);
            }
        }

        public virtual void Save(bool inTransation, params T[] objs) {
            Process(
                inTransation,
                objs,
                i => session.Save(i),
                null,
                i => {
                    collection.Add(i);
                    cache.Add(i.Id, i);
                });
        }

        public virtual void Save(params T[] objs) {
            Save(true, objs);
        }

        public virtual void Delete(bool inTransaction, params T[] objs) {
            Process(
                inTransaction,
                objs,
                obj => session.Delete(obj),
                null,
                obj => {
                    collection.Remove(obj);
                    cache.Remove(obj.Id);
                });
        }

        public virtual void Delete(params T[] objs) {
            Delete(true, objs);
        }

        public virtual void Update(bool inTransaction, params T[] objs) {
            Process(
                inTransaction, 
                objs,
                obj => session.Update(obj),
                null,
                null);
        }

        public virtual void Update(params T[] objs) {
            Update(true, objs);
        }
    }
}